#include "networkevents.h"


#ifdef FD_SETSIZE
#undef FD_SETSIZE
#endif
#define FD_SETSIZE		MAX_CONNECTIONS

#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/ioctl.h>  
#include <net/if.h> 

#ifdef HAVE_EPOLL
#include <sys/epoll.h>
#endif




#ifndef EPOLLRDHUP
/* EPOLLRDHUP was defined late in libc, and it appeared in kernel 2.6.17 */
#define EPOLLRDHUP 0x2000
#endif

#define FD_POLL_IN		0x01
#define FD_POLL_PRI	0x02
#define FD_POLL_OUT	0x04
#define FD_POLL_ERR	0x08
#define FD_POLL_HUP	0x10


struct connection {
    int fd;
    int is_udp;
	int events;

	accept_handler accept_fn;
	connect_handler connect_fn;
	data_handler data_fn;
    closed_handler closed_fn;
    udp_data_handler udp_data_fn;
	void* handler_param;

	int64_t connect_timeout;

	char* recv_buf;
	int buf_size;
	int recv_bytes;
};

struct network_events
{
#ifdef HAVE_EPOLL
	int epoll_fd;
	struct epoll_event epoll_events[MAX_CONNECTIONS];
#endif

	struct connection connections[MAX_CONNECTIONS];
	int nb_connections;
	
	int nb_connecting;
};

struct network_events ne;
int64_t now_time2;

int setnonblocking(int fd){
	if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFD, 0) | O_NONBLOCK) == -1) {
		return -1;
	}
	return 0;
}

#ifdef HAVE_EPOLL
int epoll_set(int epoll_fd, int fd, int op)
{
	struct epoll_event event;
	event.data.fd = fd;
	event.events = EPOLLIN | EPOLLRDHUP;
	return epoll_ctl(epoll_fd, op, fd, &event);
}
#endif


struct connection* find_connection(int fd)
{
	int i;
	
	for (i = 0; i < ne.nb_connections; i++) {
		if (ne.connections[i].fd == fd) {
			return (&ne.connections[i]);
		}
	}
	return NULL;
}

struct connection* new_connection(int fd, accept_handler accept_fn, data_handler data_fn, closed_handler closed_fn, void* param)
{
	if (ne.nb_connections < MAX_CONNECTIONS) {
		struct connection* conn = &ne.connections[ne.nb_connections++];
		
		conn->fd = fd;
		conn->accept_fn = accept_fn;
		conn->data_fn = data_fn;
		conn->closed_fn = closed_fn;
		conn->handler_param = param;
		conn->recv_buf = (char*)malloc(4096);
		conn->recv_bytes = 0;
		conn->buf_size = 4096;
		
		return conn;
	}
	return NULL;
}

int process_event(struct connection* conn, int events)
{
    if (conn->is_udp) {
        if (events & FD_POLL_IN) {
            struct sockaddr_in sa;
            char ip[64];
            socklen_t len;
            int ret;

            len = sizeof(sa);
            ret = recvfrom(conn->fd, conn->recv_buf, conn->buf_size, 0, (struct sockaddr*)&sa, &len);
            if (ret > 0) {
                if (conn->udp_data_fn) {
                    conn->recv_buf[ret] = 0;
                    inet_ntop(AF_INET, &sa.sin_addr.s_addr, ip, sizeof(ip));
                    ret = conn->udp_data_fn(ip, ntohs(sa.sin_port), conn->recv_buf, ret, conn->handler_param);    
                }
            }
            else
                ret = -1;

            if (ret < 0) {
                if (conn->closed_fn)
                    conn->closed_fn(-1, conn->handler_param);
                network_close(conn->fd);
            }
        }
    }
	else if (conn->accept_fn) {
		struct sockaddr_in clientaddr;
		socklen_t len = sizeof(clientaddr);
		char ip[64] = { 0 };
		int fd;

		if ((fd = accept(conn->fd, (struct sockaddr*)&clientaddr, &len)) < 0)
			return -1;
		if (ne.nb_connections >= MAX_CONNECTIONS) {
			close(fd);
			return 0;
		}
		setnonblocking(fd);
#ifdef HAVE_EPOLL
		if (ne.epoll_fd >= 0 && epoll_set(ne.epoll_fd, fd, EPOLL_CTL_ADD) < 0) {
			close(fd);
			return -1;
		}
#endif

		inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, ip, sizeof(ip));
		if (conn->accept_fn(fd, ip, ntohs(clientaddr.sin_port), conn->handler_param) < 0) {
			network_close(fd);
		}
	}
	else if (conn->connect_timeout > 0) {
		if (events & FD_POLL_OUT) {
			int error = errno;
			socklen_t len = sizeof(error);

			getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len);
			if (error == EAGAIN)
				error = 0;

			conn->connect_timeout = 0;
			ne.nb_connecting--;
			conn->connect_fn(conn->fd, error, conn->handler_param); 

			if (error) {
				network_close(conn->fd);
				return -1;
			}
		}
	}
	else {
		int err_code = 0;
		
		if (conn->recv_bytes + 1 >= conn->buf_size) {
			if (!conn->buf_size) {
				conn->recv_buf = (char*)malloc(4096);
				if (!conn->recv_buf)
					err_code = -2;
				else
					conn->buf_size = 4096;
			}
			else
				err_code = -1;
		}

		if (!err_code) {
			do {
				int ret = recv(conn->fd, conn->recv_buf + conn->recv_bytes, conn->buf_size - conn->recv_bytes - 1, 0);
				if (ret < 0) {
					int _errno = errno;
					// printf("recv(%d) error: %d\n", conn->fd, _errno);
					if (_errno == EINTR)
						continue;
					if (_errno == EAGAIN || _errno == EINPROGRESS)
						return 0;
					err_code = _errno;
				}
				else if (ret == 0) {
					err_code = ECONNRESET;
					// printf("recv() error: ECONNRESET\n");
				}
				else {
					conn->recv_bytes += ret;
					do {
    					conn->recv_buf[conn->recv_bytes] = 0;
						ret = conn->data_fn(conn->recv_buf, conn->recv_bytes, conn->handler_param);
						if (ret < 0) {
							err_code = ret;
                            break;
						}
						else if (ret > 0) {
							if (ret >= conn->recv_bytes) {
								conn->recv_bytes = 0;
							} else {
								conn->recv_bytes -= ret;
								memmove(conn->recv_buf, conn->recv_buf + ret, conn->recv_bytes);
							}
						}
                        else
                            break;
					} while (conn->recv_bytes > 0);
				}
			} while (0);
		}
		
		if (err_code) {
			conn->closed_fn(err_code, conn->handler_param);
			network_close(conn->fd);
		}
	}
	return 0;
}

int network_init()
{
	int i;
	
	memset(&ne, 0, sizeof(ne));
#ifdef HAVE_EPOLL
	ne.epoll_fd = epoll_create1(0);
	if (ne.epoll_fd < 0)
		return -1;
#endif
	for (i = 0; i < MAX_CONNECTIONS; i++) ne.connections[i].fd = -1;

	return 1;
}

void network_uninit()
{
	int i;
	for (i = 0; i < ne.nb_connections; i++) {
		network_close(ne.connections[i].fd);
	}
	
#ifdef HAVE_EPOLL
	if (ne.epoll_fd >= 0) {
		close(ne.epoll_fd);
		ne.epoll_fd = -1;
	}
#endif
}

void network_close(int fd)
{
	int i;
	
	if (fd < 0)
		return;
	
#ifdef HAVE_EPOLL
	if (ne.epoll_fd >= 0)
		epoll_set(ne.epoll_fd, fd, EPOLL_CTL_DEL);
#endif
	close(fd);
	
	for (i = 0; i < ne.nb_connections; i++) {
		struct connection* conn = &ne.connections[i];
		if (conn->fd == fd) {
			if (conn->recv_buf) {
				free(conn->recv_buf);
				conn->recv_buf = 0;
			}

			if (conn->connect_timeout > 0)
				ne.nb_connecting--;
			ne.nb_connections--;
			
			if (i < ne.nb_connections) {
				ne.connections[i] = ne.connections[ne.nb_connections];
				ne.connections[ne.nb_connections].recv_buf = 0;
				ne.connections[ne.nb_connections].fd = -1;
			}
			else
				ne.connections[i].fd = -1;
		}
	}
}

int network_listen(const char* bindip, int port, accept_handler accept_fn, closed_handler closed_fn, void* param)
{
	int fd;
	int on = 1;
	struct sockaddr_in servaddr;

	if (ne.nb_connections >= MAX_CONNECTIONS)
		return -1;
	if (port <= 0 || !accept_fn || !closed_fn)
		return -1;

	if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
		return -2;

	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	if (setnonblocking(fd) < 0) {
		close(fd);
		return -2;
	}
	
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(port);
	if (bindip) {
		servaddr.sin_addr.s_addr = inet_addr(bindip);
		if (servaddr.sin_addr.s_addr == INADDR_NONE) {
			close(fd);
			return -1;
		}
	}
	else {
		servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	}
	if (bind(fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
		close(fd);
		return -2;
	}
	
	if (listen(fd, SOMAXCONN) < 0) {
		close(fd);
		return -2;
	}
	
#ifdef HAVE_EPOLL
	if (ne.epoll_fd >= 0) {
		if (epoll_set(ne.epoll_fd, fd, EPOLL_CTL_ADD) < 0) {
			close(fd);
			return -3;
		}
	}
#endif

	if (!new_connection(fd, accept_fn, NULL, closed_fn, param)) {
		network_close(fd);
		return -1;
	}
	
	return fd;
}

int network_connect(const char* host, int port, const char* bindip, int timeout,
	connect_handler connect_fn, data_handler data_fn, closed_handler closed_fn, void* param)
{
	//获取网络协议
	struct addrinfo *result = NULL;
	const struct sockaddr *sa;
	struct sockaddr_in svraddr_4;
	struct sockaddr_in6 svraddr_6;
	struct sockaddr* addr;
	int addr_len;
	char ip_addr[128];
	socklen_t maxlen = 128;
	struct sockaddr_in bindsa;
	struct connection* conn;
	int ret;
	int fd = -1;

	if (ne.nb_connections >= MAX_CONNECTIONS)
		return -1;
	
	if (!host || !(*host) || port <= 0 || !connect_fn || !data_fn || !closed_fn)
		return -2;
	
	getaddrinfo(host, NULL, NULL, &result);
	if (!result)
		return -3;
	
	sa = result->ai_addr;
	switch (sa->sa_family) {
	case AF_INET://ipv4
		if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			break;
		}
		bzero(&svraddr_4, sizeof(svraddr_4));
		svraddr_4.sin_family = AF_INET;
		svraddr_4.sin_addr = ((struct sockaddr_in *)sa)->sin_addr;
		svraddr_4.sin_port = htons(port);
		addr_len = sizeof(svraddr_4);
		addr = (struct sockaddr*)&svraddr_4;

		memset(ip_addr, 0, sizeof(ip_addr));
		inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), ip_addr, maxlen);
		LOGI("IPV4: %s to %s\n", host, ip_addr);
		break;
	case AF_INET6://ipv6
		if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
			break;
		}

		bzero(&svraddr_6, sizeof(svraddr_6));
		svraddr_6.sin6_family = AF_INET6;
		svraddr_6.sin6_port = htons(port);
		svraddr_6.sin6_addr = ((struct sockaddr_in6 *)sa)->sin6_addr;
		addr_len = sizeof(svraddr_6);
		addr = (struct sockaddr*)&svraddr_6;

		memset(ip_addr, 0, sizeof(ip_addr));
		inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), ip_addr, maxlen);
		LOGI("IPV6: %s to %s\n", host, ip_addr);
		break;
	}
	freeaddrinfo(result);
	
	if (fd < 0) {
		return -4;
	}

	if (bindip) {
		memset(&bindsa, 0, sizeof(bindsa));
		bindsa.sin_family = AF_INET;
		bindsa.sin_port = htons(port);
		bindsa.sin_addr.s_addr = inet_addr(bindip);
		if (bindsa.sin_addr.s_addr == INADDR_NONE) {
			close(fd);
			return -5;
		}
		if (bind(fd, (struct sockaddr*)&bindsa, sizeof(bindsa)) < 0) {
			close(fd);
			return -6;
		}
	}

	if (setnonblocking(fd) < 0) {
		close(fd);
		return -7;
	}

	ret = connect(fd, addr, addr_len);
	if (ret < 0) {
		ret = errno;
		if (ret != EINPROGRESS) {
			close(fd);
			return ret ? -ret : -8;
		}
	}

	conn = new_connection(fd, NULL, data_fn, closed_fn, param);
	if (!conn) {
		close(fd);
		return -9;
	}

	conn->connect_fn = connect_fn;
	conn->connect_timeout = sys_gettime() + timeout;
	ne.nb_connecting++;
	
	return 0;
}

int network_accept(int fd, data_handler data_fn, closed_handler closed_fn, void* param)
{
	if (fd < 0 || !data_fn || !closed_fn)
		return -1;
	if (!new_connection(fd, NULL, data_fn, closed_fn, param))
		return -1;	
	return 0;
}

int network_send(int fd, const char* data, int size)
{
	int ret;

	do {
		ret = send(fd, data, size, MSG_DONTWAIT);
	} while (ret < 0 && errno == EINTR);
	 
	if (ret < 0) {
		ret = errno;
		if (ret == EINPROGRESS || ret == EAGAIN)
			return size;
	}
	return ret;
}

int network_can_send(int fd)
{
	struct connection* conn = find_connection(fd);
	if (!conn)
		return -10;
	if (conn->events & FD_POLL_OUT)
		return 1;
	return 0;
}

void network_process_events()
{
	int i;
#ifdef HAVE_EPOLL
	if (ne.epoll_fd >= 0 && ne.nb_connections > 0) {
		int count = epoll_wait(ne.epoll_fd, ne.epoll_events, MAX_CONNECTIONS, 0);
		for (i = 0; i < count; i++) {
			struct connection* conn = find_connection(ne.epoll_events[i].data.fd);
			if (conn) {
				int e = ne.epoll_events[i].events;
				int n = ((e & EPOLLIN ) ? FD_POLL_IN  : 0) |
						// ((e & EPOLLPRI) ? FD_POLL_PRI : 0) |
						((e & EPOLLOUT) ? FD_POLL_OUT : 0) |
						((e & EPOLLERR) ? FD_POLL_ERR : 0) |
						((e & EPOLLHUP) ? FD_POLL_HUP : 0);

				conn->events = n;
				if (n & (FD_POLL_IN | FD_POLL_HUP | FD_POLL_ERR)) {
					process_event(conn, n);
				}
				else if ((n & FD_POLL_OUT) && conn->connect_timeout > 0) {
					process_event(conn, n);
				}
			}
		}

		if (ne.nb_connecting > 0) {
			for (i = 0; i < ne.nb_connections; i++) {
				struct connection* conn = &ne.connections[i];
				if (conn->connect_timeout > 0 && now_time2 > conn->connect_timeout) {
					conn->connect_fn(conn->fd, ETIMEDOUT, conn->handler_param);
					network_close(conn->fd);
				}
			}
		}
		return;
	}
#endif
	
	if (ne.nb_connections > 0) {
		static struct timeval tv = { 0, 1000 };	// 1ms
		fd_set read_set, wr_set;
		int max_fd = 0;
		int ret;
		
		FD_ZERO(&read_set);
		FD_ZERO(&wr_set);
		for (i = 0; i < ne.nb_connections; i++) {
			struct connection* conn = &ne.connections[i];
			int fd = conn->fd;
			if (fd != -1) {
				if (conn->connect_timeout > 0 && now_time2 > conn->connect_timeout) {
					conn->connect_fn(fd, ETIMEDOUT, conn->handler_param);
					network_close(fd);
					continue;
				}
				FD_SET(fd, &read_set);
				FD_SET(fd, &wr_set);
				if (fd > max_fd)
					max_fd = fd;
			}
		}
		
		ret = select(max_fd + 1, &read_set, &wr_set, NULL, &tv);
		if (ret > 0) {			
			for (i = 0; i < ne.nb_connections; i++) {
				if (ne.connections[i].fd != -1) {
					int n = 0;
					if (FD_ISSET(ne.connections[i].fd, &read_set))
						n |= FD_POLL_IN;
					if (FD_ISSET(ne.connections[i].fd, &wr_set))
						n |= FD_POLL_OUT;

					ne.connections[i].events = n;
					if ((n & FD_POLL_IN) || ((n & FD_POLL_OUT) && ne.connections[i].connect_timeout > 0)) {
						process_event(&ne.connections[i], n);
					}
				}
			}
		}
	}
}

void network_setsndbuf(int fd, int val)
{
	setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val));	
}

int network_udpopen(int port, int broadcast, udp_data_handler data_fn, closed_handler closed_fn, void* param)
{
	struct sockaddr_in sa;
	struct connection* conn;
	int set_on = 1;
    int fd = -1;

    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd >= 0) {
        setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &set_on, sizeof(set_on));
        setnonblocking(fd);

        memset(&sa, 0, sizeof(sa));  
        sa.sin_family = AF_INET;  
        sa.sin_addr.s_addr = htonl(INADDR_ANY);  
        sa.sin_port = htons(port);  
        bind(fd, (struct sockaddr*)&sa, sizeof(sa));  

        if (broadcast) {
            setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &set_on, sizeof(set_on)); //设置套接字选项 
        }

        conn = new_connection(fd, NULL, NULL, closed_fn, param);
        if (!conn) {
            close(fd);
            fd = -1;
        }
        else {
            conn->is_udp = 1;
            conn->udp_data_fn = data_fn;
            ne.nb_connecting++;
        }
    }

    return fd;
}

int network_udpsend(int fd, const char* ip, int port, const char* data, int len)
{
    struct sockaddr_in sa;

    if (fd == -1 || !ip || !*ip || !data)
        return -1;

    memset(&sa, 0, sizeof(sa));  
    sa.sin_family = AF_INET;  
    sa.sin_addr.s_addr = inet_addr(ip);
    sa.sin_port = htons(port);  
    
    return sendto(fd, data, len, 0, (struct sockaddr*)&sa, sizeof(sa));
}

void network_getlocalip(char *ip)
{
    char host[128];
    struct hostent* hp;
    int i;

    *ip = 0;
    if (gethostname(host, sizeof(host)) >= 0) {
        hp = gethostbyname(host);
        if (hp) {
            for (i = 0; hp->h_addr_list[i]; i++) {
                struct in_addr* in = (struct in_addr*)hp->h_addr_list[i];
                strcpy(host, inet_ntoa(*in));
                if (strcmp(host, "127.0.0.1")) {
                    strcpy(ip, host);
                    break;
                }
            }
        }
    }
}

void network_getbroadcastip(char *ip)
{
    struct ifreq ifr;
    int fd;
    int ret;

    *ip = 0;

    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd >= 0) {
        strcpy(ifr.ifr_name, "eth0");
        ret = ioctl(fd, SIOCGIFBRDADDR, &ifr);
        close(fd);

        if (ret >= 0) {
            strcpy(ip, inet_ntoa(((struct sockaddr_in *)&(ifr.ifr_addr))->sin_addr));
        }
    }

    if (!*ip) {
        network_getlocalip(ip);
        if (*ip) {
            char* p = strrchr(ip, '.');
            if (p) {
                strcpy(p, ".255");
            }
            else
                strcpy(ip, "255.255.255.255");
        }
        else
            strcpy(ip, "255.255.255.255");
    }
}

